
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.ComponentModel;
using System.Text;
using System.IO;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;

namespace Swaf.BizObj.basic
{
	[Serializable]
	public class BizObjSorter
	{
		protected string[] m_sortFields;

		public BizObjSorter(string sortInfo)
		{
			Debug.Assert(sortInfo != null);
			m_sortFields = sortInfo.Split(',');
		}

		public object createKey(IBizObj obj)
		{
			Debug.Assert(obj != null);
			//The following if statement is used to optimize sorting on one field.
			//When sorting with one field from bizobj it is possible to have none
			//string sort options.  Once multiple fields are used for the sort,
			//the field values are converted to strings and concat to produce the
			//given key
			if(m_sortFields.Length == 1)
				return obj.get(m_sortFields[0]);
			
			//Must be a multi-field key.
            CompositeComparable cc = new CompositeComparable(false, true);
			foreach(string fld in m_sortFields)
			{
				object fldVal = obj.get(fld);
                cc.Add(fldVal);
			}

			return cc;
		}

		public string sortCriteria
		{
			get
			{
				StringBuilder ss = new StringBuilder(50);
				int len = m_sortFields.Length -1;
				for(int pos = 0; pos < len; ++pos)
				{
					ss.Append(m_sortFields[pos]);
					ss.Append(",");
				}
				if(len != -1)
					ss.Append(m_sortFields[len]);
				return ss.ToString();
			}
		}
		

	}

	[Serializable]
	public class BizObjFilterer
	{
		protected string m_filterInfo;
		protected IList values = new ArrayList();
		public BizObjFilterer(string filterInfo)
		{
			m_filterInfo = filterInfo;
			if (m_filterInfo != null && !m_filterInfo.Equals(string.Empty ))
			{
				string[] pairs = m_filterInfo.Split(',');
				int pairCount = pairs.Length;
				for (int idx = 0; idx < pairCount;idx++)
				{
					string[] nameValue = pairs[idx].Split('=');
					nameValue[1] = nameValue[1].ToLower();
					values.Add(nameValue);
				}

			}
		}

		public bool shouldFilter(IBizObj obj)
		{
			string field = "";
			string val = "";
			foreach(string[] nameValue in values)
			{
				field = nameValue[0];
				val =nameValue[1];
				//Do a case insensitive compare.
				if (! obj.get(field).ToString().ToLower().Equals(val))
					return true;
			}
			return false;
		}

		public string filterCriteria
		{
			get
			{
				return m_filterInfo;
			}
		}
	}

	/// <summary>
	/// Summary description for IndexedFullBizObjList.
	/// </summary>
	[Serializable]
	public class IndexedFullBizObjList : BaseBizObjList, IKeyedBizObjList
	{
		protected IBizObjList m_srcList;
		protected BTree m_index;
		protected BizObjSorter m_sort;
		protected BizObjFilterer m_filter;
		protected bool m_canUpdateSortFilter;
		protected bool m_sortAscending=true;
		protected PropertyDescriptor m_sortProperty=null;
		
		public IndexedFullBizObjList(IBizObjList srcList, string sortInfo, string filterInfo)
			:this(srcList, sortInfo, filterInfo, true)
		{
			//Intentionally left blank
		}

		public IndexedFullBizObjList(IBizObjList srcList, string sortInfo, string filterInfo, bool canUpdateSortFilter):base(srcList.Source)
		{
			m_srcList = srcList;
			m_sort = new BizObjSorter(sortInfo);
			m_filter = new BizObjFilterer(filterInfo);
			m_canUpdateSortFilter = canUpdateSortFilter;
			refresh();
		}
		
		public bool sortAscending{get{return m_sortAscending;}set{m_sortAscending=value;}}

		#region IBizObjList Members
		public override IBizObj this[int index]
		{
			get
			{
				return (IBizObj)m_index[m_sortAscending?index:m_index.Count-(index+1)].Value;
			}
			set
			{
				//m_index[m_sortAscending?index:m_index.Count-(index+1)] = value;
			}
		}

		public override IBizObj insertNew()
		{
			IBizObj obj = m_srcList.insertNew();
			m_index.Add((IComparable)m_sort.createKey(obj), obj);
			return obj;
		}

		public override int numElements
		{
			get
			{
				return m_index.Count;
			}
		}

		public override IBizObj insertNewMakeCopy(IBizObj obj)
		{
			IBizObj obj2 = m_srcList.insertNewMakeCopy(obj);
			m_index.Add((IComparable)m_sort.createKey(obj2), obj2);
			return obj2;
		}

        public override void Insert(int index, object value)
        {
            IBizObj obj = value as IBizObj;
            if (obj != null)
            {
                m_index.Add((IComparable)m_sort.createKey(obj), obj);
                m_srcList.Add(obj);
            }
        }

		public override int Add(object value)
		{
			IBizObj obj = value as IBizObj;
			if (obj != null)
			{
				m_index.Add((IComparable)m_sort.createKey(obj), obj);
				return m_srcList.Add(obj);
			}
			return -1;
		}

		/// <summary>
		/// Called by the main bizobj list to update its indexes.  Not to be used by outside callers.
		/// </summary>
		/// <param name="obj"></param>
		protected internal virtual void addBizObj(IBizObj obj)
		{
			m_index.Add((IComparable)m_sort.createKey(obj), obj);
		}

		public override IBizObjDefinition objectType
		{
			get
			{
				return m_srcList.objectType;
			}
		}

		public override IBizObj search(string fieldList, object matchValue)
		{
			return m_srcList.search(fieldList, matchValue);
		}

		public override IBizObj search(string fieldList, object matchValue, bool findNearestMatch)
		{
			return m_srcList.search(fieldList, matchValue,findNearestMatch);
		}


		public override void removeAt(int index)
		{
		}

		public override void refresh()
		{
			m_index = new BTree();
			for(IBizObjCursor cur = m_srcList.createCursor(); !cur.eof; cur.moveNext())
			{
				if (!m_filter.shouldFilter(cur.currentElement))
				{
					IComparable key = (IComparable)m_sort.createKey(cur.currentElement);
					if(key != null)
						m_index.Add(key, cur.currentElement);
				}
			}
			
		}

		public override IBizObjCursor createCursor()
		{
			return new IndexedFullBizObjCursor(this);
		}

		public override string getXml(string name, bool setOnly, bool dirtyOnly, bool newOnly, bool getBasic, bool getObjs, bool getILists, bool getSysVals, System.Xml.XmlTextWriter xmlOut)
		{
			return getXml(name, BaseBizObj.DefaultFieldDecoInfo, setOnly, dirtyOnly, newOnly, getBasic, getObjs, getILists, getSysVals, xmlOut);
		}
		public override string getXml(string name, string decoInfo, bool setOnly, bool dirtyOnly, bool newOnly, bool getBasic, bool getObjs, bool getILists, bool getSysVals, System.Xml.XmlTextWriter xmlOut)
		{
			bool madeXmlOut = false;
			StringWriter strBuf = null;
			if(xmlOut == null)
			{
				strBuf = new StringWriter();
				xmlOut = new XmlTextWriter(strBuf);
				madeXmlOut = true;
			}
			if (name != "")
				xmlOut.WriteStartElement(name);
			if (getSysVals)
				xmlOut.WriteAttributeString("type", m_srcList.objectType.Name);

			if (m_index.Count > 0 || !setOnly)
				for (int pos = 0; pos < m_index.Count; ++pos)
				{
					IBizObj obj = (IBizObj) ((IBizObjList)this)[pos];
					obj.getXml(null, decoInfo, setOnly, dirtyOnly, newOnly, getBasic, getObjs, getILists, getSysVals, xmlOut);
				}

			if (name !="")
				xmlOut.WriteEndElement();
			if(madeXmlOut)
			{
				xmlOut.Close();
				strBuf.Close();
				return strBuf.ToString();
			}
			return null;
		}



		#endregion

		#region IKeyedBizObjList Members

		public IBizObj this[IComparable sortBasedKey]
		{
			get
			{
				if (sortBasedKey is Int32)
					return ((IBizObjList) this)[(int) sortBasedKey];

				int pos = m_index.Rank(sortBasedKey, false);
				if(pos != -1)
					return (IBizObj) m_index[pos].Value;
				return null;
			}
		}

		public string Sort
		{
			get
			{
				return m_sort.sortCriteria;
			}
			set
			{
				if(!m_canUpdateSortFilter)
					throw new NotSupportedException("The Sort property cannot be changed on this object");
				m_sort = new BizObjSorter(value);
				refresh();
			}
		}

		public IBizObj find(IComparable sortBasedKey, bool nearestMatchOk)
		{
			int pos = m_index.Rank(sortBasedKey, nearestMatchOk);
			if(pos != -1)
				return (IBizObj) m_index[pos].Value;
			return null;
		}

		public string Filter
		{
			get
			{
				return m_filter.filterCriteria;
			}
			set
			{
				if(!m_canUpdateSortFilter)
					throw new NotSupportedException("The Filter property cannot be changed on this object");
				m_filter = new BizObjFilterer(value);
				refresh();
			}
		}

		public bool canChangeSortFilter{get{return m_canUpdateSortFilter;}}
		public override void Clear()
		{
			m_index = new BTree();
		}
		#endregion

		#region ICloneable Members

		public override object Clone()
		{
			// TODO:  Add IndexedFullBizObjList.Clone implementation
			return null;
		}

		#endregion
		#region IBindingList Members

		public override void AddIndex(PropertyDescriptor property) {throw new NotImplementedException();}
		public override bool AllowNew {get{return true;}}
		public override void ApplySort(PropertyDescriptor property, ListSortDirection direction) 
		{
			m_sort = new BizObjSorter(property.Name);
			m_sortAscending = (direction == ListSortDirection.Ascending);
			m_sortProperty=property;
			refresh();
		}
		public override PropertyDescriptor SortProperty {get{return m_sortProperty;}}
		public override int Find(PropertyDescriptor property, object key) {throw new NotImplementedException();}
		public override bool SupportsSorting {get{return true;}}
		public override bool IsSorted {get{return true;}}
		public override bool AllowRemove {get{return true;}}
		public override bool SupportsSearching {get{return false;}}
		public override ListSortDirection SortDirection {get{ return m_sortAscending?ListSortDirection.Ascending:ListSortDirection.Descending;}}
		public override event ListChangedEventHandler ListChanged;
		public override bool SupportsChangeNotification {get{return false;}}
		public override void RemoveSort() {throw new NotImplementedException();}
		public override object AddNew()	{return insertNew();}
		public override bool AllowEdit {get{return true;}}
		#endregion

	}
}
